<!DOCTYPE html>
<html>

<head>
  <title>Quarkus and Web UI Development</title>
  <script id="adobe_dtm" src="https://www.redhat.com/dtm.js" type="text/javascript"></script>
  <script src="/assets/javascript/highlight.pack.js" type="text/javascript"></script>
  <META HTTP-EQUIV='Content-Security-Policy' CONTENT="default-src 'none'; script-src 'self' 'unsafe-eval' 'sha256-ANpuoVzuSex6VhqpYgsG25OHWVA1I+F6aGU04LoI+5s=' 'sha256-ipy9P/3rZZW06mTLAR0EnXvxSNcnfSDPLDuh3kzbB1w=' js.bizographics.com https://www.redhat.com assets.adobedtm.com jsonip.com https://ajax.googleapis.com https://www.googletagmanager.com https://www.google-analytics.com https://use.fontawesome.com; style-src 'self' https://fonts.googleapis.com https://use.fontawesome.com; img-src 'self' *; media-src 'self' ; frame-src https://www.googletagmanager.com https://www.youtube.com; frame-ancestors 'none'; base-uri 'none'; object-src 'none'; form-action 'none'; font-src 'self' https://use.fontawesome.com https://fonts.gstatic.com;">
  <META HTTP-EQUIV='X-Frame-Options' CONTENT="DENY">
  <META HTTP-EQUIV='X-XSS-Protection' CONTENT="1; mode=block">
  <META HTTP-EQUIV='X-Content-Type-Options' CONTENT="nosniff">
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="Quarkus: Supersonic Subatomic Java">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@QuarkusIO"> 
  <meta name="twitter:creator" content="@QuarkusIO">
  <meta property="og:url" content="https://quarkus.io/blog/quarkus-and-web-ui-development-mode/" />
  <meta property="og:title" content="Quarkus and Web UI Development" />
  <meta property="og:description" content="Quarkus: Supersonic Subatomic Java" />
  <meta property="og:image" content="/assets/images/quarkus_card.png" />
  <link rel="canonical" href="https://quarkus.io/blog/quarkus-and-web-ui-development-mode/">
  <link rel="shortcut icon" type="image/png" href="/favicon.ico" >
  <link rel="stylesheet" href="https://quarkus.io/guides/stylesheet/config.css" />
  <link rel="stylesheet" href="/assets/css/main.css" />
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
  <link rel="alternate" type="application/rss+xml"  href="https://quarkus.io/feed.xml" title="Quarkus">
  <script src="https://quarkus.io/assets/javascript/goan.js" type="text/javascript"></script>
  <script src="https://quarkus.io/assets/javascript/hl.js" type="text/javascript"></script>
</head>

<body class="post">
  <!-- Google Tag Manager (noscript) -->
  <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-NJWS5L"
  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
  <!-- End Google Tag Manager (noscript) -->

  <div class="nav-wrapper">
  <div class="grid-wrapper">
    <div class="width-12-12">
      <input type="checkbox" id="checkbox" />
      <nav id="main-nav" class="main-nav">
  <div class="container">
    <div class="logo-wrapper">
      
        <a href="/"><img src="/assets/images/quarkus_logo_horizontal_rgb_600px_reverse.png" class="project-logo" title="Quarkus"></a>
      
    </div>
    <label class="nav-toggle" for="checkbox">
      <i class="fa fa-bars"></i>
    </label>
    <div id="menu" class="menu">
      <span>
        <a href="/get-started/" class="">Get Started</a>
      </span>
      <span>
        <a href="/guides/" class="">Guides</a>
      </span>
      <span>
        <a href="/community/" class="">Community</a>
      </span>
      <span>
        <a href="/support/" class="">Support</a>
      </span>
      <span>
        <a href="/blog/" class="active">Blog</a>
      </span>
      <span>
        <a href="https://code.quarkus.io" class="button-cta secondary white">Start Coding</a>
      </span>
    </div>
  </div>
      </nav>
    </div>
  </div>
</div>

  <div class="content">
    <div class="post-page grid-wrapper">
  <div class="width-8-12 width-12-12-m doc-content">
    <div class="grid-wrapper">
      <div class="width-12-12">
        <p>
          <a href="/blog"><i class="fas fa-angle-left"></i> Back to all posts</a>
        </p>
      </div>
      <div class="width-12-12">
        <div class="post-date">
          June 05, 2019 
          
            <span class="tags"><a href="/blog/tag/development-tips">#development-tips</a></span>
          
        </div>
        <h1 class="post-title">Quarkus and Web UI Development</h1>
        <div class="grid-wrapper">
          <div class="width-8-12 width-12-12-m byline-wrapper">
            
            
              <img class="headshot" src="https://www.gravatar.com/avatar/3da43b71f2d9e45a079af1d1f3e8bfff">
            
            <p class="byline">By Kabir Khan</p>
          </div>
          <div class="width-12-12">
              <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>In this blog post we will take advantage of the respective development modes of both Quarkus and Angular CLI and see how we can develop a zero turnaround web application backed by a RESTful API on Quarkus. While I am using Angular, <strong>the concepts are the same for other modern web application frameworks.</strong></p>
</div>
<div class="paragraph">
<p>The source code for this blog can be found at <a href="https://github.com/kabir/blog-quarkus-ui-development" class="bare">https://github.com/kabir/blog-quarkus-ui-development</a>. It contains a README which explains in detail how the application was set up and how we implemented each step, addressing problems we uncovered along our way.</p>
</div>
<div class="paragraph">
<p>You need to have <a href="https://nodejs.org/en/download/package-manager/">Node</a>, <a href="https://yarnpkg.com/en/docs/install">Yarn</a> and <a href="https://cli.angular.io">Angular CLI</a> installed on your system, and familiarity with Angular and Quarkus is assumed.</p>
</div>
<div class="paragraph">
<p>Clone the <a href="https://github.com/kabir/blog-quarkus-ui-development">project</a>, and go into its folder. First run:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$mvn -Dui.deps compile</pre>
</div>
</div>
<div class="paragraph">
<p>This downloads all the dependencies needed to build our web application. Then run:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$mvn package -Dui.dev -Dui.proxy quarkus:dev</pre>
</div>
</div>
<div class="paragraph">
<p>The <code>-Dui</code> system property enables a Maven profile to build the web application. The output directory for the web application <code>webapp/dist/webapp</code> gets bundled into the Quarkus application. We will talk about <code>-Dui.proxy</code> later on.</p>
</div>
<div class="paragraph">
<p>Go to <a href="http://localhost:8080" class="bare">http://localhost:8080</a> in your browser and take a look at our sample application. It contains a few different pages. Go to the one called <code>Rest</code> which makes a REST call. We can change the return value of the <a href="https://github.com/kabir/blog-quarkus-ui-development/blob/master/src/main/java/org/kabir/quarkus/ui/SampleResource.java"><code>SampleServlet.hello()</code></a> method. If you refresh the page you will see the changes made reflected automatically!</p>
</div>
<div class="paragraph">
<p>With what we have so far, if we were to make changes to the user interface in our web application, we would need to stop Quarkus and run the above command to build the web application again and restart quarkus. This is not what we want, so we take advantage of Angular&#8217;s <a href="https://angular.io/guide/build#proxying-to-a-backend-server">proxy</a>. <a href="https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development">React</a> and <a href="https://cli.vuejs.org/config/#devserver-proxy">Vue</a> have something similar.</p>
</div>
<div class="paragraph">
<p>In another terminal window go into the <code>webapp/</code> folder of the cloned project, and run:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$yarn proxy</pre>
</div>
</div>
<div class="paragraph">
<p>This will start the Angular development server in proxy mode on port 4200. Go to <a href="http://localhost:4200" class="bare">http://localhost:4200</a> and you will see the same application as you saw earlier on port 8080, but served by the Angular development server while accessing the REST endpoints from the running Quarkus application. If you make any changes to any of the Angular components set up in <a href="https://github.com/kabir/blog-quarkus-ui-development/blob/master/webapp/src/app/app.component.ts"><code>app.component.ts</code></a> you will see your changes reflected.</p>
</div>
<div class="paragraph">
<p>This is great!!! We can now be super-productive and make changes in both our front-end and our back-end, and see the changes immediately without needing to rebuild, repackage and restart our application!</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="adjustments-summary"><a class="anchor" href="#adjustments-summary"></a>Adjustments Summary</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The example project has already been set up with the needed adjustments to work properly both as a bundled application and in development mode. These are the main tweaks:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Handle the Angular routes in the UI rather than on the back-end. This is important for the bundled version (running on port 8080).</p>
</li>
<li>
<p>Enable the Angular proxy to allow for the UI to call REST endpoints served by Quarkus.</p>
<div class="olist loweralpha">
<ol class="loweralpha" type="a">
<li>
<p>Signal to the back-end that the front-end is running in a proxy, i.e. not served by us. This is important if the back-end needs to redirect to a page in the front-end.</p>
</li>
</ol>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>We will focus on the changes needed to make this work well, and not go into the details of how the application is set up.</p>
</div>
<div class="sect2">
<h3 id="handle-angular-routes"><a class="anchor" href="#handle-angular-routes"></a>Handle Angular Routes</h3>
<div class="paragraph">
<p>Add a filter, <a href="https://github.com/kabir/blog-quarkus-ui-development/blob/master/src/main/java/org/kabir/quarkus/ui/AngularRouteFilter.java">AngularRouteFilter</a>. The essence of its <code>doFilter()</code> method is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code data-lang="java" class="language-java hljs">chain.doFilter(request, response);

if (response.getStatus() == 404) {
    String path = request.getRequestURI().substring(
            request.getContextPath().length()).replaceAll("[/]+$", "");
    if (!FILE_NAME_PATTERN.matcher(path).matches()) {
        // We could not find the resource, i.e. it is not anything known to the server (i.e. it is not a REST
        // endpoint or a servlet), and does not look like a file so try handling it in the front-end routes.
        request.getRequestDispatcher("/").forward(request, response);
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This filter is only needed when running the application bundled in Quarkus (port 8080). It is not needed when connecting to the application running in Angular (port 4200). It&#8217;s purpose is to allow us to <strong>bookmark pages</strong> in the application. All the server knows about are things like:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the REST endpoints</p>
</li>
<li>
<p>deployed servlets</p>
</li>
<li>
<p>the resources in the <code>META-INF/resources/</code> folder of the application</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The <code>META-INF/resources/</code> in our case contains the application&#8217;s index.html and the transpiled resources.</p>
</div>
<div class="paragraph">
<p>Without this filter, if you go to one of the Angular routes directly (e.g if you try to refresh when on <a href="http://localhost:8080/rest" class="bare">http://localhost:8080/rest</a> which is the <code>Rest</code> page we saw earlier) Quarkus will think it is a file, a REST endpoint or a servlet. Since Quarkus has no knowledge of anything called <code>/rest</code> you end up with a 404 (Not Found) status. The filter checks the status code of the request after calling <code>chain.doFilter()</code>. If the status is 404 and does not seem to be a file, we forward this request to <code>/</code>, which in turn serves up the <code>index.html</code> file. By doing a forward the path and parameters of the request are preserved. Angular then figures out that <code>/rest</code> is one of its known routes and displays the appropriate page of the application. Once the web application is loaded in the browser, Angular takes over and handles all the internal links to other routes in the web application (as an example, if you are on <a href="http://localhost:8080" class="bare">http://localhost:8080</a> and click on the link taking you to <a href="http://localhost:8080/other" class="bare">http://localhost:8080/other</a> there is no round-trip to the server).</p>
</div>
<div class="paragraph">
<p>There are other ways you can handle this too, e.g. by checking the path against a set of hard-coded known paths that are to be handled by the back-end, but for my purposes the above has worked very well. The key is to invoke:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>request.getRequestDispatcher("/").forward(request, response);</pre>
</div>
</div>
<div class="paragraph">
<p>if it is something that should be handled by Angular.</p>
</div>
</div>
<div class="sect2">
<h3 id="set-up-the-angular-proxy"><a class="anchor" href="#set-up-the-angular-proxy"></a>Set up the Angular proxy</h3>
<div class="paragraph">
<p>The proxy is configured in <a href="https://github.com/kabir/blog-quarkus-ui-development/blob/master/webapp/proxy.conf.json">proxy.conf.json</a>. All REST calls to anything under <code>/api</code> will be passed to the back-end running on port 8080. To run the Angular development server with this configuration, we have added a <code>proxy</code> configuration to the <code>scripts</code> section of <a href="https://github.com/kabir/blog-quarkus-ui-development/blob/master/webapp/package.json"><code>package.json</code></a>.</p>
</div>
<div class="paragraph">
<p>In our case, we have a servlet which needs to redirect back to the front-end (something I found I needed to implement OAuth in my main project). That has a check for the <code>-Dui.proxy</code> system property we saw earlier when handling the <code>/callback</code> path in <a href="https://github.com/kabir/blog-quarkus-ui-development/blob/master/src/main/java/org/kabir/quarkus/ui/SampleServlet.java"><code>SampleServlet</code></a>. If this property is set, we prepend <code><a href="https://localhost:4200" class="bare">https://localhost:4200</a></code> (the address of the Angular proxy) to the redirect URL if we find the proxy is running on port 4200.</p>
</div>
<div class="paragraph">
<p>Finally, <code>DefaultComponent</code> in <a href="https://github.com/kabir/blog-quarkus-ui-development/blob/master/webapp/src/app/app.component.ts"><code>app.component.ts</code></a> has a direct link to our servlet running in the back-end. It has a check to see if the web application is running in the proxy (i.e. its port is 4200), and if this is the case it adjusts the url from <code>/servlet/make-external-call</code> to point to the Quarkus back-end running on port 8080.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="last-words"><a class="anchor" href="#last-words"></a>Last words</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We have seen how with a minimum amount of configuration we can have both the back-end and front-end of our application running in Quarkus&#8217;s and Angular&#8217;s respective development modes. During development this removes the need to rebuild the application when we change something. You can just code away, and see the changes when you refresh your browser. This is done by starting Quarkus with:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$mvn package -Dui.dev -Dui.proxy quarkus:dev</pre>
</div>
</div>
<div class="paragraph">
<p>and starting the Angular application (from the <code>webapp/</code> folder) with</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$yarn proxy</pre>
</div>
</div>
<div class="sect2">
<h3 id="packaging-for-development"><a class="anchor" href="#packaging-for-development"></a>Packaging for development</h3>
<div class="paragraph">
<p>If you want to do a rebuild now and again to package the application and run it all in Quarkus run</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$mvn package -Dui.dev quarkus:dev</pre>
</div>
</div>
<div class="paragraph">
<p>By not passing in -Dui.proxy we disable the checks for whether the front-end runs in a proxy. <code>-Dui.dev</code> builds the web application so it is part of the Quarkus application.</p>
</div>
</div>
<div class="sect2">
<h3 id="packaging-for-production"><a class="anchor" href="#packaging-for-production"></a>Packaging for production</h3>
<div class="paragraph">
<p>To package the application for production, use</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$mvn package -Dui</pre>
</div>
</div>
<div class="paragraph">
<p><code>-Dui</code> builds the web application just like <code>-Dui.dev</code>, but it does more optimisations for production. Those optimisations take some time.</p>
</div>
</div>
<div class="sect2">
<h3 id="packaging-for-cloud-native"><a class="anchor" href="#packaging-for-cloud-native"></a>Packaging for cloud native</h3>
<div class="paragraph">
<p>Finally to build a native image, make sure you have set up GraalVM as outlined <a href="https://quarkus.io/guides/building-native-image">here</a>. Then build the application to generate the native executable.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$mvn package -Dui -Pnative</pre>
</div>
</div>
<div class="paragraph">
<p>Running this we see the super-fast startup time Quarkus gives us in native mode:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>$./target/blog-quarkus-ui-development-0.1.0-runner
2019-06-06 10:57:02,254 INFO  [io.quarkus] (main) Quarkus 0.15.0 started in 0.005s. Listening on: http://[::]:8080
2019-06-06 10:57:02,464 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy, resteasy-jsonb]</pre>
</div>
</div>
</div>
</div>
</div>
              
          </div>
          <div class="width-12-12"><div class="share-page">
  <a class="share-linkedin" href="https://www.linkedin.com/shareArticle?mini=true&url=https://quarkus.io/blog/quarkus-and-web-ui-development-mode/&title=Quarkus and Web UI Development" rel="nofollow" target="_blank" title="Share on LinkedIn">
    <img src="/assets/images/share-page/icons_social-linkedin.png"/>
  </a>
  <a class="share-twitter" href="https://twitter.com/intent/tweet?text=Quarkus and Web UI Development&url=https://quarkus.io/blog/quarkus-and-web-ui-development-mode/&via=quarkusio&related=quarkusio" rel="nofollow" target="_blank" title="Share on Twitter">
    <img src="/assets/images/share-page/icons_social-twitter.png"/>
  </a>
  <a class="share-facebook" href="https://facebook.com/sharer.php?u=https://quarkus.io/blog/quarkus-and-web-ui-development-mode/" rel="nofollow" target="_blank" title="Share on Facebook">
    <img src="/assets/images/share-page/icons_social-facebook.png"/>
  </a>
  <a class="share-reddit" href="http://www.reddit.com/submit?url=https://quarkus.io/blog/quarkus-and-web-ui-development-mode/" onclick="window.open(this.href, 'pop-up', 'left=20,top=20,width=900,height=500,toolbar=1,resizable=0'); return false;" title="Share on Reddit" >
    <img src="/assets/images/share-page/icons_social-reddit.png"/>
  </a>
  <a class="share-email" href="mailto:?subject=Quarkus and Web UI Development&amp;body=Quarkus and Web UI Development https://quarkus.io/blog/quarkus-and-web-ui-development-mode/" title="Share via Email" >
    <img src="/assets/images/share-page/icons_social-email.png"/>
  </a>
</div>
</div>
        </div>
      </div>
    </div>
  </div>
</div>

  </div>

  <div class="content project-footer">
  <div class="footer-section">
    <div class="logo-wrapper">
      <a href="/"><img src="/assets/images/quarkus_logo_horizontal_rgb_reverse.svg" class="project-logo" title="Quarkus"></a>
    </div>
  </div>
  <div class="grid-wrapper">
    <p class="grid__item width-3-12">Quarkus is open. All dependencies of this project are available under the <a href='https://www.apache.org/licenses/LICENSE-2.0' target='_blank'>Apache Software License 2.0</a> or compatible license.<br /><br />This website was built with <a href='https://jekyllrb.com/' target='_blank'>Jekyll</a>, is hosted on <a href='https://pages.github.com/' target='_blank'>Github Pages</a> and is completely open source. If you want to make it better, <a href='https://github.com/quarkusio/quarkusio.github.io' target='_blank'>fork the website</a> and show us what you’ve got.</p>

    
      <div class="width-1-12 project-links">
        <span>Navigation</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="/">Home</a></li>
          
            <li><a href="/guides">Guides</a></li>
          
            <li><a href="/community/#contributing">Contribute</a></li>
          
            <li><a href="/faq">FAQ</a></li>
          
            <li><a href="/get-started">Get Started</a></li>
          
        </ul>
      </div>
    
      <div class="width-1-12 project-links">
        <span>Contribute</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="https://twitter.com/quarkusio">Follow us</a></li>
          
            <li><a href="https://github.com/quarkusio">GitHub</a></li>
          
            <li><a href="/security">Security&nbsp;policy</a></li>
          
        </ul>
      </div>
    
      <div class="width-1-12 project-links">
        <span>Get Help</span>
        <ul class="footer-links width-1-12">
          
            <li><a href="https://groups.google.com/forum/#!forum/quarkus-dev">Forums</a></li>
          
            <li><a href="https://quarkusio.zulipchat.com">Chatroom</a></li>
          
        </ul>
      </div>
    

    
      <div class="width-3-12 more-links">
        <span>Quarkus is made of community projects</span>
        <ul class="footer-links">
          
            <li><a href="https://vertx.io/" target="_blank">Eclipse Vert.x</a></li>
          
            <li><a href="https://microprofile.io" target="_blank">Eclipse MicroProfile</a></li>
          
            <li><a href="https://hibernate.org" target="_blank">Hibernate</a></li>
          
            <li><a href="https://netty.io" target="_blank">Netty</a></li>
          
            <li><a href="https://resteasy.github.io" target="_blank">RESTEasy</a></li>
          
            <li><a href="https://camel.apache.org" target="_blank">Apache Camel</a></li>
          
            <li><a href="https://code.quarkus.io/" target="_blank">And many more...</a></li>
          
        </ul>
      </div>
    
  </div>
</div>
  <div class="content redhat-footer">
  <div class="grid-wrapper">
    <span class="licence">
      <i class="fab fa-creative-commons"></i><i class="fab fa-creative-commons-by"></i> <a href="https://creativecommons.org/licenses/by/3.0/" target="_blank">CC by 3.0</a> | <a href="https://www.redhat.com/en/about/privacy-policy">Privacy Policy</a>
    </span>
    <span class="redhat">
      Sponsored by
    </span>
    <span class="redhat-logo">
      <a href="https://www.redhat.com/" target="_blank"><img src="/assets/images/redhat_reversed.svg"></a>
    </span>
  </div>
</div>


  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js" integrity="sha384-8gBf6Y4YYq7Jx97PIqmTwLPin4hxIzQw5aDmUg/DDhul9fFpbbLcLh3nTIIDJKhx" crossorigin="anonymous"></script>
  <script type="text/javascript" src="/assets/javascript/mobile-nav.js"></script>
  <script type="text/javascript" src="/assets/javascript/scroll-down.js"></script>
  <script src="/assets/javascript/satellite.js" type="text/javascript"></script>
  <script src="https://quarkus.io/guides/javascript/config.js" type="text/javascript"></script>
  <script src="/assets/javascript/search-filter.js" type="text/javascript"></script>
  <script src="/assets/javascript/back-to-top.js" type="text/javascript"></script>
</body>

</html>
